package cn.codeforfun.initialization.server;

import cn.codeforfun.initialization.command.CommandService;
import cn.codeforfun.initialization.common.Pagination;
import cn.codeforfun.initialization.server.exception.ServerConnectException;
import cn.codeforfun.initialization.server.model.Server;
import cn.codeforfun.initialization.server.model.ServerRepository;
import cn.codeforfun.initialization.thread.ThreadService;
import cn.codeforfun.initialization.utils.JschUtil;
import com.jcraft.jsch.JSchException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.configurationprocessor.json.JSONException;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.util.Map;
import java.util.Optional;

import static cn.codeforfun.initialization.server.ServerConstants.*;

/**
 * @author wangbin
 */
@Service
@Slf4j
public class ServerService {
    private static final String CENTOS = "centos";
    private static final String ID = "ID";
    @Resource
    private CommandService commandService;
    @Resource
    private ServerRepository serverRepository;
    @Resource
    private ThreadService threadService;

    Pagination<Server> listServer(Pagination<Server> pagination) {
        PageRequest pageRequest = PageRequest.of(pagination.getPageNo() - 1, pagination.getPageSize());
        Page<Server> all = serverRepository.findAll(pageRequest);
        pagination.setData(all.getContent());
        pagination.setCount(all.getTotalElements());
        return pagination;
    }

    void saveServer(Server server) {
        serverRepository.save(server);
    }

    Server getServer(Integer id) {
        Optional<Server> server = serverRepository.findById(id);
        return server.orElse(null);
    }

    boolean connectServer(Server server) throws JSchException {
        try {
            JschUtil jsch = new JschUtil(server);
            jsch.disconnect();
            return true;
        } catch (ServerConnectException e) {
            return false;
        }
    }

    public boolean isServerHostExist(String host) {
        Server server = new Server();
        server.setHost(host);
        Example<Server> example = Example.of(server);
        Optional<Server> one = serverRepository.findOne(example);
        return one.isPresent();
    }

    void deleteServer(Integer id) {
        serverRepository.deleteById(id);
    }

    Server refreshServer(Integer id) throws JSchException, JSONException {
        Server server = serverRepository.getOne(id);
        JschUtil jsch = new JschUtil(server);
        Map<String, Object> systemVersion = commandService.getSystemVersion(jsch);
        String kernelVersion = commandService.getKernelVersion(jsch);
        String dockerVersion = commandService.getDockerVersion(jsch);
        String helmVersion = commandService.getHelmVersion(jsch);
        String kubectlVersion = commandService.getKubectlVersion(jsch);
        jsch.disconnect();

        server.setOsId((String) systemVersion.get("ID"));
        server.setOsVersionId((String) systemVersion.get("VERSION_ID"));
        server.setOsPrettyName((String) systemVersion.get("PRETTY_NAME"));
        server.setOsKernelVersion(kernelVersion);

        server.setDockerVersion(dockerVersion);
        server.setDockerFlag(ObjectUtils.isEmpty(dockerVersion) ? UNINSTALL : INSTALLED);
        server.setHelmVersion(helmVersion);
        server.setHelmFlag(ObjectUtils.isEmpty(helmVersion) ? UNINSTALL : INSTALLED);
        server.setKubectlVersion(kubectlVersion);
        server.setKubectlFlag(ObjectUtils.isEmpty(kubectlVersion) ? UNINSTALL : INSTALLED);

        serverRepository.save(server);
        return serverRepository.getOne(id);
    }

    private void installDocker(Server server) throws JSchException {
        JschUtil jsch1 = new JschUtil(server);
        String dockerVersion = commandService.getDockerVersion(jsch1);
        if (!ObjectUtils.isEmpty(dockerVersion)) {
            server.setDockerVersion(dockerVersion);
            server.setDockerFlag(INSTALLED);
            serverRepository.save(server);
            return;
        }
        server.setDockerFlag(INSTALLING);
        serverRepository.save(server);
        jsch1.disconnect();
        threadService.execute(() -> {
            JschUtil jsch = null;
            try {
                jsch = new JschUtil(server);
                commandService.installDocker(jsch);
                if (CENTOS.equals(commandService.getSystemVersion(jsch).get(ID))) {
                    commandService.centOsStartDocker(jsch);
                }
                server.setDockerVersion(commandService.getDockerVersion(jsch));
                server.setDockerFlag(INSTALLED);
            } catch (JSchException e) {
                log.error(e.getMessage());
                server.setDockerFlag(INSTALLED_FAILED);
                e.printStackTrace();
            } finally {
                serverRepository.save(server);
                if (jsch != null) {
                    jsch.disconnect();
                }
            }
        });
    }

    private void installHelm(Server server) throws JSchException, JSONException {
        JschUtil jsch1 = new JschUtil(server);
        String helmVersion = commandService.getHelmVersion(jsch1);
        if (!ObjectUtils.isEmpty(helmVersion)) {
            server.setHelmVersion(helmVersion);
            server.setHelmFlag(INSTALLED);
            serverRepository.save(server);
            return;
        }
        server.setHelmFlag(INSTALLING);
        serverRepository.save(server);
        jsch1.disconnect();
        threadService.execute(() -> {
            JschUtil jsch = null;
            try {
                jsch = new JschUtil(server);
                commandService.installHelm(jsch);
                server.setHelmVersion(commandService.getHelmVersion(jsch));
                server.setHelmFlag(INSTALLED);
            } catch (JSchException | JSONException e) {
                log.error(e.getMessage());
                server.setHelmFlag(INSTALLED_FAILED);
                e.printStackTrace();
            } finally {
                serverRepository.save(server);
                if (jsch != null) {
                    jsch.disconnect();
                }
            }
        });
    }

    private void installKubectl(Server server) throws JSchException, JSONException {
        JschUtil jsch1 = new JschUtil(server);
        String kubectlVersion = commandService.getKubectlVersion(jsch1);
        if (!ObjectUtils.isEmpty(kubectlVersion)) {
            server.setKubectlVersion(kubectlVersion);
            server.setKubectlFlag(INSTALLED);
            serverRepository.save(server);
            return;
        }
        server.setKubectlFlag(INSTALLING);
        serverRepository.save(server);
        jsch1.disconnect();
        threadService.execute(() -> {
            JschUtil jsch = null;
            try {
                jsch = new JschUtil(server);
                commandService.installKubectl(jsch);
                server.setKubectlVersion(commandService.getKubectlVersion(jsch));
                server.setKubectlFlag(INSTALLED);
            } catch (JSchException | JSONException e) {
                log.error(e.getMessage());
                server.setKubectlFlag(INSTALLED_FAILED);
                e.printStackTrace();
            } finally {
                serverRepository.save(server);
                if (jsch != null) {
                    jsch.disconnect();
                }
            }
        });
    }

    void installSoft(Integer id, String type) throws JSchException, JSONException {
        Server server = serverRepository.getOne(id);
        switch (type) {
            case "docker": {
                installDocker(server);
                break;
            }
            case "helm": {
                installHelm(server);
                break;
            }
            case "kubectl": {
                installKubectl(server);
                break;
            }
            default:
        }
    }
}
